﻿using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Micro.Extensions.Utilities;
using Microsoft.Extensions.Logging;

namespace Micro.Extensions.Data
{
    public class CommandProxy : DbCommand
    {
        private readonly DbCommand _dbCommand;
        private readonly ILogger _logger;
        private readonly int _executeSqlElapsedTimeWarning;
        private readonly bool _isLogEnable;
        public CommandProxy(DbCommand dbCommand, int executeSqlElapsedTimeWarning,ILogger logger)
        {
            _dbCommand = dbCommand;
            _logger = logger;
            _executeSqlElapsedTimeWarning = executeSqlElapsedTimeWarning;
            _isLogEnable = _logger.IsEnabled(LogLevel.Debug);
        }
        public override void Cancel()
        {
            _dbCommand.Cancel();
        }

        public override int ExecuteNonQuery()
        {
            var sw = Stopwatch.StartNew();
            try
            {
                return _dbCommand.ExecuteNonQuery();
            }
            finally
            {
                LogCommand(sw.ElapsedMilliseconds);
            }

        }




        public override async Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken)
        {
            var sw = Stopwatch.StartNew();
            try
            {
                return await _dbCommand.ExecuteNonQueryAsync(cancellationToken);
            }
            finally
            {
                LogCommand(sw.ElapsedMilliseconds);
            }


        }

        public override object ExecuteScalar()
        {
            var sw = Stopwatch.StartNew();
            try
            {
                return _dbCommand.ExecuteScalar();
            }
            finally
            {
                LogCommand(sw.ElapsedMilliseconds);
            }

        }

        public override async Task<object> ExecuteScalarAsync(CancellationToken cancellationToken)
        {
            var sw = Stopwatch.StartNew();
            try
            {
                return await _dbCommand.ExecuteScalarAsync(cancellationToken);
            }
            finally
            {
                LogCommand(sw.ElapsedMilliseconds);
            }

        }

        public override void Prepare()
        {
            _dbCommand.Prepare();
        }

        public override string CommandText { get => _dbCommand.CommandText; set => _dbCommand.CommandText = value; }
        public override int CommandTimeout { get => _dbCommand.CommandTimeout; set => _dbCommand.CommandTimeout = value; }
        public override CommandType CommandType { get => _dbCommand.CommandType; set => _dbCommand.CommandType = value; }
        public override UpdateRowSource UpdatedRowSource { get => _dbCommand.UpdatedRowSource; set => _dbCommand.UpdatedRowSource = value; }
        protected override DbConnection DbConnection { get => _dbCommand.Connection; set => _dbCommand.Connection = value; }
        protected override DbParameterCollection DbParameterCollection => _dbCommand.Parameters;
        protected override DbTransaction DbTransaction { get => _dbCommand.Transaction; set => _dbCommand.Transaction = value; }
        public override bool DesignTimeVisible { get => _dbCommand.DesignTimeVisible; set => _dbCommand.DesignTimeVisible = value; }

        protected override DbParameter CreateDbParameter()
        {
            return _dbCommand.CreateParameter();
        }

        protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
        {
            var sw = Stopwatch.StartNew();
            try
            {
                return _dbCommand.ExecuteReader(behavior);
            }
            finally
            {
                LogCommand(sw.ElapsedMilliseconds);
            }

        }

        protected override async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
        {
            var sw = Stopwatch.StartNew();
            try
            {
                return await _dbCommand.ExecuteReaderAsync(behavior, cancellationToken);
            }
            finally
            {
                LogCommand(sw.ElapsedMilliseconds);
            }

        }
        /// <summary>
        /// 获取参数
        /// </summary>
        /// <returns></returns>
        private IEnumerable<KeyValuePair<string, object>> GetParameters()
        {
            return _dbCommand.Parameters.Cast<DbParameter>()
                .Select(s => new KeyValuePair<string, object>(s.ParameterName, s.Value));
        }

        private void LogCommand(long elapsedMilliseconds)
        {

            //记录耗费时间超过预警值的sql
            if (elapsedMilliseconds > _executeSqlElapsedTimeWarning)
            {
                _logger.LogDataWarn(new
                {
                    _dbCommand.CommandText,
                    Params= GetParameters(),
                    TimeSpan= elapsedMilliseconds
                }.ToJson());
                return;
            }
            //只有debug模式下才记录正常sql
            if (_isLogEnable)
            {
                _logger.LogDataDebug(new
                {
                    _dbCommand.CommandText,
                    Params = GetParameters(),
                    TimeSpan = elapsedMilliseconds
                }.ToJson());
            }
        }
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _dbCommand?.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}
